package com.way.filemanager.navigation;

import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;


import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.os.FileObserver;
import android.os.Handler;
import android.os.StatFs;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.util.Log;
import android.widget.Toast;
import com.way.filemanager.R;
import com.way.filemanager.activities.GetContextActivity;
import com.way.filemanager.model.ParentDirectory;
import com.way.filemanager.navigation.NavigationListFragmant.SelectMode;
import com.way.filemanager.navigation.Utils.FileBytesListener;
import com.way.filemanager.navigation.Utils.FileFT;
import com.way.filemanager.navigation.Utils.FileFTs;
import com.way.filemanager.providers.FolderInfoDBHelper;
import com.way.filemanager.ui.policy.IntentsActionPolicy;
import com.way.filemanager.util.CommonFunc;
import com.way.filemanager.util.CommonIdDef;
import com.way.filemanager.util.FileHelper;

public class MyFileManager
{
    public interface FileListener
    {
        void onFile(File file);
    }

    public interface FileChangeListener
    {
        void onChanged();
    }

    public interface FileProgressListener
    {
        void onPreProgressStart(SelectMode mode);
        void onProgress(File f, int p, int total);
        void onProgress(File f,long currentbytes, long totalbytes);
        void onProgressDone();
    }

    public interface IFileOperation
    {
        public File[] getCurrentDirFiles();
        public File getCurrentDir();
        public boolean goUpFolder();
        public boolean openFolder(File file);
        public void reOrderCurrentDirFiles();
        public void readCurrentDirFiles();
        public boolean mkDir(String name);
        public void onResume();
        public void onPause();
        public String getRootName();
        public String getRootPath();
    }

    public enum FileOrder
    {
        name, size, date, type
    }

    public final static int MAX_PROGRESS = 100;

    private final static String DEFAULT_FILE_ORDER_STRING = FileOrder.name.toString();
    private final static String SAVE_KEY_SHOWHIDDEN = "ShowHidden";
    private final static boolean DEFAULT_FILE_SHOW_HIDDEN = false;

    private final static String TAG = "fm";
    private static MyFileManager fm;
    private final static int MIN_SPACE = 5;
    public static final MyFileManager getFm() { return fm; }

    public static void onCreate(Context c)
    {
        if(fm == null)
        {
            fm = new MyFileManager(c);
        }
    }

    public static void onTerminate()
    {
        fm.onDestroy();
        fm = null;
    }

    public final void toast(final String str)
    {
        mHandler.post(new Runnable(){
            @Override
            public void run() {
                Toast.makeText(mContext, str, Toast.LENGTH_LONG).show();
            }
        });
    }

    public final void toast(final int str)
    {
        mHandler.post(new Runnable(){
            @Override
            public void run() {
                Toast.makeText(mContext, str, Toast.LENGTH_LONG).show();
            }
        });
    }

    public static final void log(String str) { Log.i(TAG, str);}
    public static final void log(String str, Throwable tr) { Log.e(TAG, str, tr); }

    private Context mContext;
    private FileOrder mOrder;
    private boolean mIsShowHiden;

    private StorageHelper mSotrageHelper;
    private Handler mHandler;


    public MyFileManager(Context context)
    {
        mContext = context;
        onInit();
    }

    private void onInit()
    {
        mHandler = new Handler();
        loadSaved();
        mSotrageHelper = new StorageHelper();
        mMyCache = new MyCache(mContext);
        //initSubTitleTable();
    }

    private void onDestroy()
    {
        mSotrageHelper.onDestroy();
        mContext = null;
        destroySubTitleTable();
    }

    public void onPause()
    {
        mSotrageHelper.onPause();
    }

    public void onResume()
    {
        mSotrageHelper.onResume();
    }

    private void loadSaved()
    {
        SharedPreferences sp = mContext.getSharedPreferences(CommonIdDef.SAVE_KEY_PREFERENCE, Context.MODE_PRIVATE);
        mOrder = FileOrder.valueOf(sp.getString(CommonIdDef.SAVE_KEY_ORDER, DEFAULT_FILE_ORDER_STRING));
        mIsShowHiden = sp.getBoolean(SAVE_KEY_SHOWHIDDEN, DEFAULT_FILE_SHOW_HIDDEN);
    }

    public FileOrder getOrder() { return mOrder; }
    public void saveOrder(FileOrder o)
    {
        if(o == mOrder)
        {
            return;
        }
        mOrder = o;
        SharedPreferences sp = mContext.getSharedPreferences(CommonIdDef.SAVE_KEY_PREFERENCE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(CommonIdDef.SAVE_KEY_ORDER, mOrder.toString());
        editor.commit();

        mSotrageHelper.reOrderCurrentDirFiles();
    }
    public boolean isShowHidden() { return mIsShowHiden; }
    public void saveShowHidden(boolean showHidden)
    {
        if(showHidden == mIsShowHiden)
        {
            return;
        }
        mIsShowHiden = showHidden;
        SharedPreferences sp = mContext.getSharedPreferences(CommonIdDef.SAVE_KEY_PREFERENCE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putBoolean(SAVE_KEY_SHOWHIDDEN, mIsShowHiden);
        editor.commit();

        mSotrageHelper.readCurrentDirFiles();
    }

    public void setFileChangeListener(FileChangeListener l) { mSotrageHelper.setListener(l); }
    public File getCurrentDir() { return mSotrageHelper.getCurrentDir(); }
    public File[] getFiles() { return mSotrageHelper.getCurrentDirFiles(); }
    public boolean goUpFolder() { return mSotrageHelper.goUpFolder(); }
    public boolean mkDir(String name) { return mSotrageHelper.mkDir(name); }
    public void goRootFolder() { mSotrageHelper.goRootFolder(); }
    public String getRootName() { return mSotrageHelper.getRootName(); }
    public String getRootPath() { return mSotrageHelper.getRootPath(); }
    public boolean openFolder(File folder) { return mSotrageHelper.openFolder(folder); }
    public boolean isCurrentRoot() { return mSotrageHelper.isCurrentRoot(); }


    public boolean onFileClicked(Context c, File file)
    {
        if(file == null)
        {
            return false;
        }

        if(file.isDirectory())
        {
            mSotrageHelper.openFolder(file);
            return true;
        }
        else
        {
            if((c instanceof GetContextActivity))
            {
                Intent mIntent = new Intent();
                mIntent.setData(Uri.fromFile(file));
                ((GetContextActivity) c).setResult(Activity.RESULT_OK, mIntent);
                ((GetContextActivity) c).finish();
                return false;
            }

            IntentsActionPolicy.openFileSystemObject(c, FileHelper.createFileSystemObject(file),false,null,null);
        }
        return false;

    }

    private final FileFilter mHiddenFilter = new FileFilter() { @Override public boolean accept(File f) { return !f.isHidden(); }};

    private class StorageHelper implements MyFileListerListener, IFileOperation
    {
        private final RootFileLister mRootFileLister = new RootFileLister();
        private IFileOperation mCurrent;
        private LinkedList<MyFileLister> mFileListers = new LinkedList<MyFileLister>();
        private FileChangeListener mFileChangeListener;
        private StorageManager mStorageManager;
        private Method mVolumeListMethod;
        private Method mVolumeStateMethod;
        private Method mVolumeRegisterListener;
        private Method mVolumeUnRegisterListener;

        public StorageHelper()
        {
            setCurrentDir(mRootFileLister);
            onStart();
        }

        public boolean isCurrentRoot()
        {
            return (mCurrent == mRootFileLister);
        }

        public void onChanged(IFileOperation fl)
        {
            if(fl == mCurrent && mFileChangeListener != null)
            {
                mFileChangeListener.onChanged();
            }
        }
        public void setListener(FileChangeListener l) { mFileChangeListener = l; }
        public File[] getCurrentDirFiles() { return mCurrent.getCurrentDirFiles(); }
        public File getCurrentDir() { return mCurrent.getCurrentDir(); }
        public boolean goUpFolder() { return mCurrent.goUpFolder();}
        public boolean openFolder(File file) { return mCurrent.openFolder(file); }
        public void reOrderCurrentDirFiles() { mCurrent.reOrderCurrentDirFiles(); }
        public void readCurrentDirFiles() { mCurrent.readCurrentDirFiles(); }
        public boolean mkDir(String n) { return mCurrent.mkDir(n); }
        public void goRootFolder() { setCurrentDir(mRootFileLister); }
        public String getRootName() { return mCurrent.getRootName(); }
        public String getRootPath() { return mCurrent.getRootPath(); }

        public void setCurrentDir(IFileOperation fl)
        {
            if(fl != null)
            {
                if(mCurrent != null)
                {
                    mCurrent.onPause();
                }
                mCurrent = fl;
                mCurrent.onResume();
            }
        }

        public void onStart()
        {
            try {
                mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
                mVolumeListMethod = mStorageManager.getClass().getMethod("getVolumeList");
                mVolumeStateMethod = mStorageManager.getClass().getMethod("getVolumeState", String.class);
                mVolumeRegisterListener = mStorageManager.getClass().getMethod("registerListener", StorageEventListener.class);
                mVolumeUnRegisterListener = mStorageManager.getClass().getMethod("unregisterListener", StorageEventListener.class);
                onUpdate();
            } catch (Exception e) {
                log("fail in get volume ", e);
            }
        }
        public void onDestroy()
        {
            try {
                mVolumeUnRegisterListener.invoke(mStorageManager, mStorageEventListener);
            } catch (Exception e) {
                log("fail in onDestroy ", e);
            }
        }
        public void onPause()
        {
            try {
                mVolumeUnRegisterListener.invoke(mStorageManager, mStorageEventListener);
            } catch (Exception e) {
                log("fail in onPause ", e);
            }
        }
        public void onResume()
        {
            try {
                mVolumeRegisterListener.invoke(mStorageManager, mStorageEventListener);
                onUpdate();
            } catch (Exception e) {
                log("fail in onResume ", e);
            }
        }

        private void onUpdate()
        {
            try {
                StorageVolume[] sv = (StorageVolume[]) mVolumeListMethod.invoke(mStorageManager);
                for(StorageVolume s : sv)
                {
                    String path = s.getPath();
                    String state = (String) mVolumeStateMethod.invoke(mStorageManager, path);
                    boolean isMounted = Environment.MEDIA_MOUNTED.equals(state);
                    int id = s.getDescriptionId();
                    log("path: "+s.getPath() +", id "+s.getDescription(mContext)+", remove " +s.isRemovable() + ", state "+state);

                    if(new File(path).isDirectory() == false)
                    {
                        log("got failed path: "+path+" is not a directory");
                        continue;
                    }

                    MyFileLister fl = hasSameFileLister(path, id);
                    if(fl != null && isMounted == false)
                    {
                        if(mCurrent == fl)
                        {
                            setCurrentDir(mRootFileLister);
                        }
                        fl.onDestroy();
                        mFileListers.remove(fl);
                    }
                    else if(fl == null && isMounted == true)
                    {
                        fl = new MyFileLister(path);
                        fl.id = id;
                        mFileListers.add(fl);
                        fl.setListener(this);
                    }
                }
                //bugID:99822
                Collections.sort(mFileListers,new Comparator<MyFileLister>(){
                    @Override
                    public int compare(MyFileLister lhs, MyFileLister rhs) {
                        // TODO Auto-generated method stub
                        return rhs.getRootPath().compareTo(lhs.getRootPath());
                    }

                });
                onChanged(mCurrent);

            } catch (Exception e) {
                log("fail in onUpdate ", e);
            }
        }

        private StorageEventListener mStorageEventListener = new StorageEventListener()
        {
             public void onUsbMassStorageConnectionChanged(boolean connected) { onUpdate(); }
             public void onStorageStateChanged(String path, String oldState, String newState) { onUpdate(); }
        };

        private MyFileLister hasSameFileLister(String path, int id)
        {
            for(MyFileLister fl : mFileListers)
            {
                if(fl.id == id && fl.mRootPath.equals(path))
                {
                    return fl;
                }else if(fl.id == id || fl.mRootPath.equals(path)){
                    //bug114048:delete the unuse FileLister when unmount the SD card
                    if(mCurrent == fl)
                    {
                        setCurrentDir(mRootFileLister);
                    }
                    fl.onDestroy();
                    mFileListers.remove(fl);
                }
            }
            return null;
        }

        private class RootFileLister implements IFileOperation
        {
            private class MyRootFile extends File
            {
                public int id;
                public MyRootFile(MyFileLister fl)
                {
                    super(fl.mRootPath);
                    id = fl.id;
                }
                @Override public String getName() { return mContext.getResources().getString(id); }
                @Override public boolean canExecute() { return false; }
                @Override public boolean canRead() { return false; }
                @Override public boolean canWrite() { return false; }
                private static final long serialVersionUID = 1222343L;
                public MyFileLister getFl()
                {
                    for(MyFileLister fl : mFileListers)
                    {
                        if(fl.id == id && fl.mRootPath.equals(getAbsolutePath()))
                        {
                            return fl;
                        }
                    }
                    return null;
                }
            }

            private class MyRootFolder extends File
            {
                public MyRootFolder() { super(File.separator); }
                private static final long serialVersionUID = 134544233L;
                @Override public boolean canExecute() { return false; }
                @Override public boolean canRead() { return false; }
                @Override public boolean canWrite() { return false; }
            }

            private final File TOP_PATH = new MyRootFolder();
            @Override
            public File[] getCurrentDirFiles()
            {
                int num = mFileListers.size();
                File[] files = new File[num];
                for(int i = 0; i < num ; i++)
                {
                    files[i] = new MyRootFile(mFileListers.get(i));
                }

                if(num >1)
                {
                    MyFileLister m = mFileListers.get(0);
                    if(m.mRootPath.equals(m.currentDir.getAbsolutePath()))
                        return files;
                }
                return reorderFiles(files);
            }
            @Override public File getCurrentDir() { return TOP_PATH; }
            @Override public boolean goUpFolder() { return false; }
            @Override
            public boolean openFolder(File file)
            {
                Log.d(TAG, "RootFileLister openFolder");
                if(file instanceof MyRootFile)
                {
                    File[] fileList;
                    MyFileLister fl = ((MyRootFile)file).getFl();
                    if(fl != null)
                    {
                        fl.resetCurrentDir();
                        setCurrentDir(fl);
                        fileList = fl.getCurrentDirFiles();
                        initSubTitleTable(fl.getRootPath(), fileList);
                    }
                }
                return false;
            }
            @Override public void reOrderCurrentDirFiles() { onChanged(RootFileLister.this); }
            @Override public void readCurrentDirFiles() { onChanged(RootFileLister.this); }
            @Override public boolean mkDir(String name) {  return false; }
            @Override public void onResume() { onChanged(RootFileLister.this); }
            @Override public void onPause() {}
            @Override public String getRootName() { return ""; }
            @Override public String getRootPath() { return File.separator; }
        }

        private class MyFileLister implements IFileOperation
        {
            public int id = -1;

            private final String mRootPath;
            private final static int FILEOBSERVE_EVENT = FileObserver.CREATE | FileObserver.DELETE
                    | FileObserver.DELETE_SELF | FileObserver.MOVED_FROM | FileObserver.MOVED_TO | FileObserver.MOVE_SELF;
            private File currentDir;
            private File[] currentDirFiles;
            private FileObserver mFileObserver;
            private MyFileListerListener mFileChangeListener;
            private boolean isPaused;

            public MyFileLister(String rootPath)
            {
                isPaused = true;
                mRootPath = rootPath;
                currentDir = new File(rootPath);
                if(currentDir.isDirectory() == false)
                {
                    log("got path is not dirctory, path: " + rootPath);
                    goRootFolder();
                }
            }

            public void resetCurrentDir()
            {
                currentDir = new File(mRootPath);
                onCurrentDirChanged();
                readCurrentDirFiles();
            }
            public void onDestroy()
            {
                if(mFileObserver != null)
                {
                    mFileObserver.stopWatching();
                    mFileObserver = null;
                }
                mFileChangeListener = null;
            }
            public void onResume()
            {
                isPaused = false;
                if(mFileObserver == null)
                {
                    newFileObserver();
                }
                mFileObserver.startWatching();
                readCurrentDirFiles();
            }

            public void onPause()
            {
                isPaused = true;
                if(mFileObserver != null)
                {
                    mFileObserver.stopWatching();
                }
            }

            public File[] getCurrentDirFiles() { return currentDirFiles;  }
            public File getCurrentDir() { return currentDir;  }
            public String getRootPath() { return mRootPath; }
            public boolean goUpFolder()
            {
                if(mRootPath.equals(currentDir.getAbsolutePath()) == false)
                {
                    File f = currentDir.getParentFile();
                    if(f != null)
                    {
                        currentDir = f;
                    }
                    else
                        currentDir = new File(mRootPath);
                    onCurrentDirChanged();
                    readCurrentDirFiles();
                }
                else
                {
                    mCurrent = mRootFileLister;
                    onChanged(mCurrent);
                }
                return true;
            }

            public boolean openFolder(File file)
            {
                Log.d(TAG, "MyFileLister openFolder");
                if(file.isDirectory() == false)
                {
                    log("openFolder not folder: "+file.getAbsolutePath());
                    return false;
                }

                if(file.getAbsolutePath().startsWith(mRootPath) == false)
                {
                    log("openFolder got root: "+mRootPath+", file: "+file.getAbsolutePath());
                    goRootFolder();
                    return false;
                }

                currentDir = file;
                onCurrentDirChanged();
                readCurrentDirFiles();
                return true;
            }

            public void reOrderCurrentDirFiles()
            {
                currentDirFiles = reorderFiles(currentDirFiles);
                notifyFileChanged();
            }

            public void setListener(MyFileListerListener l) { mFileChangeListener = l; }
            private void notifyFileChanged()
            {
                if(mFileChangeListener != null)
                {
                    mFileChangeListener.onChanged(this);
                }
            }

            @SuppressWarnings("deprecation")
            public boolean mkDir(String name)
            {
                File newDir = new File(currentDir.getAbsolutePath() + File.separator +name);
                if(newDir.exists()&&newDir.isDirectory())
                {
                    toast(R.string.ali_dirIsExist);
                    return false;
                }
                else
                {
                    boolean ret = new File(currentDir.getAbsolutePath() + File.separator +name).mkdir();
                    if(!ret)
                    {
                        //bug 114977,modify mkdir failed toast
                        File parentDir = new File(currentDir.getAbsolutePath());
                        if(parentDir.getFreeSpace() > 0)
                            toast(R.string.ali_dirNameEmtpy);
                        else
                            toast(R.string.ali_NotEnoughSpace);
                    }
                    return ret;
                }
            }

            private void newFileObserver()
            {
                if(mFileObserver != null)
                {
                    mFileObserver.stopWatching();
                    mFileObserver = null;
                }
                mFileObserver = new FileObserver(currentDir.getAbsolutePath())
                {
                    @Override
                    public void onEvent(int event, String path)
                    {
                        //log("got event "+path+", e "+event);
                        if((event & FILEOBSERVE_EVENT) != 0)
                        {
                            mHandler.post(new Runnable() { @Override public void run() { readCurrentDirFiles(); }});
                        }
                    }
                };
            }

            private void onCurrentDirChanged()
            {
                newFileObserver();
                if(isPaused == false)
                {
                    mFileObserver.startWatching();
                }
            }

            public void readCurrentDirFiles()
            {
                if(currentDir.isDirectory() == false)
                {
                    resetCurrentDir();
                    return;
                }

                File[] files;
                if(mIsShowHiden == false)
                {
                    files = currentDir.listFiles(mHiddenFilter);
                }
                else
                {
                    files = currentDir.listFiles();
                }
                if(files == null)
                {
                    files =  new File[] {};
                }

                currentDirFiles = reorderFiles(files);
                notifyFileChanged();
            }
            public String getRootName() { return mContext.getResources().getString(id); }
        }
    }



    public interface MyFileListerListener
    {
        void onChanged(IFileOperation fl);
    }

    private File[] reorderFiles(File[] files)
    {
        /*switch(CommonFunc.loadSaved(mContext))
        {
            default:
            case name:
                return Utils.reorderFilesByName(files);
            case size:
                return Utils.reorderFilesBySize(files);
            case date:
                return Utils.reorderFilesByTime(files);
            case type:
                return Utils.reorderFilesByType(files);
        }*/
        FileOrder mOrder = CommonFunc.loadSaved(mContext);
        final int sortmode = CommonFunc.fileOrder2Index(mOrder);
        List<File> mWorkingList = new ArrayList<File>();
        for(File m:files)
        {
            mWorkingList.add(m);
        }
        Collections.sort(mWorkingList, new Comparator<File>() {
            @Override
            public int compare(File lhs, File rhs) {
                // Need to sort directory first?
                if (true) {
                    boolean isLhsDirectory = lhs.isDirectory();
                    boolean isRhsDirectory = rhs.isDirectory();
                    if (isLhsDirectory || isRhsDirectory) {
                        if (isLhsDirectory && isRhsDirectory) {
                            // Apply sort mode
                            return FileHelper.doCompare(lhs, rhs, sortmode);
                        }
                        return (isLhsDirectory) ? -1 : 1;
                    }
                }

                // Apply sort mode
                return FileHelper.doCompare(lhs, rhs, sortmode);
            }

        });
        int len = files.length;
        for(int count= 0;count <len;count++)
        {
            files[count] = mWorkingList.get(count);
        }
        mWorkingList.clear();
        mWorkingList = null;
        return files;
    }

    //file process

    private boolean mIsWorking;


    private FileProgressListener mFileProgressListener;
    private boolean mCancelFileProcess;

    public void cancelFileProcess()
    {
        synchronized(MyFileManager.this)
        {
            mCancelFileProcess = true;
        }
    }

    private class DeleteThread extends Thread
    {
        protected LinkedList<File> mSelectedFiles;
        private File mCurrentProcessFile;
        private int mProgress;
        private int mTotal;
        private Runnable mNotify = new Runnable() { @Override public void run()
        {
            if(mFileProgressListener != null)
            {
                mFileProgressListener.onProgress(mCurrentProcessFile, mProgress, mTotal);
            }
        }};

        public DeleteThread(LinkedList<File> files)
        {
            super();
            long num = 0;
            mSelectedFiles = files;
            for(File m :files)
            {
                num= CommonFunc.listDirFileNum(m,num);//files.size();
            }
            mTotal = (int)num;
            mProgress = 0;
        }

        protected SelectMode getSelectMode()
        {
            return SelectMode.delete;
        }

        private void onPreProgress()
        {
            mHandler.post(new Runnable()
            {
                @Override
                public void run()
                {
                    if(null != mFileProgressListener)
                    {
                        mFileProgressListener.onPreProgressStart(getSelectMode());
                        //bug102895
                        if(mSelectedFiles.size()>0)
                            mFileProgressListener.onProgress(mSelectedFiles.get(0), mProgress, mTotal);
                    }
                }
            });
        }

        protected boolean processFile(File file)
        {
            log("delete "+file.getAbsolutePath());
            return file.delete();

        }

        private void postProgress(final File f, final int p)
        {
            mCurrentProcessFile = f;
            mProgress = p;
            mHandler.removeCallbacks(mNotify);
            mHandler.post(mNotify);
        }

        @Override
        public void run()
        {
            synchronized(MyFileManager.this)
            {
                if(mSelectedFiles == null || mSelectedFiles.size() == 0)
                {
                    log("DeleteThread param fail");
                    mIsWorking = false;
                    return;
                }
                if(mCancelFileProcess == true)
                {
                    log("DeleteThread cancel before run");
                    return;
                }
            }



            mSelectedFiles = Utils.listAllDeleteFiles(mSelectedFiles);
            int p = 0;
            final int len = mSelectedFiles.size();
            onPreProgress();
            CommonFunc.StartDeleteMediaFile();
            for(File f : mSelectedFiles)
            {
                mProgress ++;
                postProgress(f, mProgress/*p*MAX_PROGRESS/len*/);
                CommonFunc.DeleteMediaFile(f);
                processFile(f);
                p++;
                if(mCancelFileProcess == true)
                {
                    log("got cancel");
                    break;
                }
            }
            CommonFunc.EndDeleteMediaFile(mContext,false);
            synchronized(MyFileManager.this)
            {
                mIsWorking = false;

                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if(mFileProgressListener != null)
                        {
                            mFileProgressListener.onProgressDone();
                        }
                    }
                });
            }
        }
    };

    private class CrushDeleteThread extends DeleteThread
    {
        public CrushDeleteThread(LinkedList<File> files) { super(files); }
        protected boolean processFile(File file)
        {
            log("crush "+file.getAbsolutePath());
            CommonFunc.StartDeleteMediaFile();
            if(file.isDirectory() == false)
            {
                crushFile(file);
                CommonFunc.DeleteMediaFile(file);
            }
            else
            {
                file.delete();
            }
            CommonFunc.EndDeleteMediaFile(mContext,false);
            return true;
        }

        @Override
        protected SelectMode getSelectMode()
        {
            return SelectMode.crush_delete;
        }
    }

    public boolean doDeleteFiles(LinkedList<File> files, FileProgressListener l)
    {
        synchronized(MyFileManager.this)
        {
            if(mIsWorking)
            {
                return false;
            }
        }

        if(files == null || files.size() == 0)
        {
            return false;
        }

        synchronized(MyFileManager.this)
        {
            mIsWorking = true;
            mFileProgressListener = l;
            mCancelFileProcess = false;
            new DeleteThread(files).start();
        }
        log("begin delete");

        return true;
    }

    public boolean doCrushDeleteFiles(LinkedList<File> files, FileProgressListener l)
    {
        synchronized(MyFileManager.this)
        {
            if(mIsWorking)
            {
                return false;
            }
        }

        if(files == null || files.size() == 0)
        {
            return false;
        }

        synchronized(MyFileManager.this)
        {
            mIsWorking = true;
            mFileProgressListener = l;
            mCancelFileProcess = false;
            new CrushDeleteThread(files).start();
        }

        log("begin crush delete");
        return true;
    }

    private class CopyThread extends Thread implements FileBytesListener
    {
        protected LinkedList<File> mSelectedFiles;
        protected File toFolder;
        private long mTotalBytes;
        private long mProcessedBytes;
        private File mCurrentProcessFile;
        private int mProgress;

        // peikai.zpk add
        private int mCurrentCopyedFileCount;
        private int mTotalCopyFileCount;

        public CopyThread(LinkedList<File> from , File to)
        {
            super();
            mSelectedFiles = from;
            toFolder = to;
        }

        private Runnable mNotify = new Runnable() { @Override public void run()
        {
            if(null != mFileProgressListener)
            {
					Log.d("hans", "mTotalCopyFileCount -1-"+ mTotalCopyFileCount);
					if (mTotalCopyFileCount == 1) {
						mFileProgressListener.onProgress(mCurrentProcessFile,mProcessedBytes, mTotalBytes);
					} else {
						mFileProgressListener.onProgress(mCurrentProcessFile,mCurrentCopyedFileCount, mTotalCopyFileCount);
					}
            }
        }};

        protected SelectMode getSelectMode()
        {
            return SelectMode.copy;
        }

        private void onPreProgress()
        {
            mHandler.post(new Runnable()
            {
                @Override
                public void run()
                {
                    if(null != mFileProgressListener)
                    {
						mFileProgressListener.onPreProgressStart(getSelectMode());
						Log.d("hans", "mTotalCopyFileCount --"+ mTotalCopyFileCount);
						if (mTotalCopyFileCount == 1) {
							mFileProgressListener.onProgress(mCurrentProcessFile, mProcessedBytes,mTotalBytes);
						} else {
							mFileProgressListener.onProgress(mCurrentProcessFile,mCurrentCopyedFileCount,mTotalCopyFileCount);
						}
					}
                }
            });
        }

        protected boolean processFile(FileFT ft)
        {
            if(Utils.copyListedFile(ft.file, ft.to, this) == false)
            {
                log("fail in copy "+ft.file.getAbsolutePath() + ", to "+ft.to.getAbsolutePath());
                toast(R.string.ali_NotEnoughSpace);
                return false;
            }
            return true;
        }

        private void postProgress(final File f, final int p)
        {
            mCurrentProcessFile = f;
            mProgress = p;
            mHandler.removeCallbacks(mNotify);

            mHandler.post(mNotify);
        }

        @Override
        public boolean onProgress(long bytes)
        {
            if(mCurrentProcessFile != null)
            {
                mProcessedBytes += bytes;
                int p = (int) ((mProcessedBytes * MAX_PROGRESS) /mTotalBytes);
                if(p < 0)
                    p = 0;
                else if(p > MAX_PROGRESS)
                    p = MAX_PROGRESS;
                if(mProgress != p)
                {
                    mProgress = p;
                    postProgress(mCurrentProcessFile, p);
                }
                return !mCancelFileProcess;
            }
            return true;
        }

        @Override
        public void run()
        {
            synchronized(MyFileManager.this)
            {
                if(mSelectedFiles == null || mSelectedFiles.size() == 0 || toFolder == null || toFolder.isDirectory() == false
                        || toFolder.canWrite() == false)
                {
                    mIsWorking = false;
                    log("CopyThread param fail");
                    return;
                }
                if(mCancelFileProcess == true)
                {
                    log("CopyThread got cancel before run");
                    return;
                }

            }



            FileFTs ft = Utils.listFileForCopy(mSelectedFiles, toFolder);
            mProgress = -1;
            mProcessedBytes = 0;
            mTotalBytes = ft.length;

            // peikai.zpk begin add.
            mTotalCopyFileCount = ft.size();
            mCurrentCopyedFileCount = 0;
            // peikai.zpk end add.
            onPreProgress();
            CommonFunc.StartDeleteMediaFile();
            for(FileFT f : ft)
            {
                mCurrentProcessFile = f.file;
                if(!processFile(f))
                {
                    Log.v(TAG, "processFile fail");
                    break;
                }
                CommonFunc.DeleteMediaFile(f.to);
                // peikai.zpk begin add.
                ++mCurrentCopyedFileCount;
                mHandler.post(mNotify);
                // peikai.zpk add end.

                if(mCancelFileProcess == true)
                {
                    log("CopyThread got cancel");
                    break;
                }
            }
            CommonFunc.EndDeleteMediaFile(mContext,false);
            log("copy done");
            OnCopyDone();

            synchronized(MyFileManager.this)
            {
                mIsWorking = false;
                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if(mFileProgressListener != null)
                        {
                            mFileProgressListener.onProgressDone();
                        }
                    }
                });
            }
        }
        protected void OnCopyDone() {}

    };

    public boolean doCopyFiles(LinkedList<File> files, FileProgressListener l)
    {
        synchronized(MyFileManager.this)
        {
            if(mIsWorking)
            {
                return false;
            }
        }

        if(files == null || files.size() == 0)
        {
            return false;
        }

        synchronized(MyFileManager.this)
        {
            mIsWorking = true;
            mFileProgressListener = l;
            mCancelFileProcess = false;
            new CopyThread(files, mSotrageHelper.getCurrentDir()).start();
        }
        log("begin copy");

        return true;
    }

    private class CutThread extends CopyThread
    {
        public CutThread(LinkedList<File> files, File to)
        {
            super(files, to);
        }

        @Override
        protected boolean processFile(FileFT ft)
        {
            if(super.processFile(ft))
            {
                log("cut delete "+ft.file.getAbsolutePath());
                ft.file.delete();
                return true;
            }
            return false;
        }

        @Override
        protected void OnCopyDone()
        {
            for(File f : mSelectedFiles)
            {
                log("delete "+f.getAbsolutePath());
                //f.delete();
                delDirOncutDone(f);
            }
        }

        @Override
        protected SelectMode getSelectMode()
        {
            return SelectMode.cut;
        }

        private boolean delDirOncutDone(File f)
        {
            if (f.isDirectory()) {
                String[] children = f.list();
                for (int i=0; i<children.length; i++) {
                    boolean ret = delDirOncutDone(new File(f, children[i]));
                    if (!ret) {
                        return false;
                    }
                }
            }
            return f.delete();
        }
    };

    public boolean doCutFiles(LinkedList<File> files, FileProgressListener l)
    {
        synchronized(MyFileManager.this)
        {
            if(mIsWorking)
            {
                return false;
            }
        }

        if(files == null || files.size() == 0)
        {
            return false;
        }

        synchronized(MyFileManager.this)
        {
            mIsWorking = true;
            mFileProgressListener = l;
            mCancelFileProcess = false;
            new CutThread(files, mSotrageHelper.getCurrentDir()).start();
        }
        log("begin copy");
        return true;
    }


    // folder subtitle

    private HashMap<String, String> mSubTitleTable = new HashMap<String, String>();
    private void destroySubTitleTable() { mSubTitleTable.clear(); }
    private void putSubTitle(String path, String info) { mSubTitleTable.put(path.toLowerCase(Locale.getDefault()), info);}
    private void initSubTitleTable(String rootPath, File[] file)
    {
        FolderInfoDBHelper DBhelper = new FolderInfoDBHelper(mContext);
        SQLiteDatabase db = DBhelper.getWritableDatabase();
        for(File tmpFile: file) {
            if(tmpFile.isDirectory()) {
                String fileName = File.separator + tmpFile.getName();
                Cursor c = db.rawQuery("SELECT * FROM dir_name WHERE path = ?", new String[]{fileName});
                if(c == null)
                    continue;
                while(c.moveToNext()) {
                    String path = c.getString(c.getColumnIndex("path"));
                    String name = c.getString(c.getColumnIndex("name"));
                    if(path.compareTo(File.separator + tmpFile.getName()) == 0) {
                        putSubTitle(rootPath + fileName, name);
                    }
                }
                c.close();
            }
        }
        db.close();
    }
    public String getFolderSubTitle(File file)
    {
        String info = mSubTitleTable.get(file.getAbsolutePath().toLowerCase(Locale.getDefault()));
        return info;
    }

    private MyCache mMyCache;

    public Drawable getFileIcon(String name, File file)
    {
        return mMyCache.getFileIcon(name, file);
    }

    public void setCacheIconCb(FileChangeListener l)
    {
        mMyCache.setListener(l);
    }

    private final static int LENGTH = 256*1024;
    private  void crushFile(File f)
    {
        if(f == null || !f.exists())
            return;
        byte mBuffer[] = new byte[LENGTH];
        long length = f.length();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(f);
            while((length > 0)&& (!mCancelFileProcess))
            {
                if(length >= LENGTH) {
                    fos.write(mBuffer,0,LENGTH);
                }else{
                    fos.write(mBuffer,0,(int)length);
                }
                length -= LENGTH;
            }
            fos.close();
            fos = null;
            f.delete();
        }catch(Exception e) {
            MyFileManager.log("crushFile fail", e);
        } finally {
            try {
                if(fos != null)
                    fos.close();
            } catch (Exception e) {
                e.printStackTrace();
                MyFileManager.log("crushFile close fail", e);
            }

        }
    }
}
